﻿using Castle.Core.Configuration;
using Castle.Facilities.NHibernateIntegration;
using Inovout.Models;
using Inovout.Repositories;
using Inovout.Runtime;
using Inovout.Web.Http;
using Inovout.Web.Mvc;
using Inovout.Web.Security;
using Microsoft.Web.WebPages.OAuth;
using System;
using System.Collections.Generic;
using System.Net.Http.Formatting;
using System.Reflection;
using System.Web;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;

namespace Inovout.Web
{
    public class WebApplication : HttpApplication, IApplication, IWebApplication
    {
        //每个请求都会有一个Application，并不是唯一，所以不能Dispose全局数据，如：URLMAP
        //BeginRequest
        //AuthenticateRequest
        //PostAuthenticateRequest
        //AuthorizeRequest
        //PostAuthorizeRequest
        //ResolveRequestCache
        //PostResolveRequestCache
        //MapRequestHandler
        //PostMapRequestHandler
        //AcquireRequestState
        //PostAcquireRequestState
        //PreRequestHandlerExecute
        //PostRequestHandlerExecute
        //ReleaseRequestState
        //PostReleaseRequestState
        //UpdateRequestCache
        //PostUpdateRequestCache
        //LogRequest
        //PostLogRequest
        //EndRequest
        //PreSendRequestContent
        //PreSendRequestHeaders
        //RequestCompleted
        public virtual bool IsDebug
        {
            get { return HttpContext.Current.IsDebuggingEnabled; }
        }

        public DynamicDictionary Settings { get { return this.applicationWrapper.Settings; } }
        public string Title
        {
            get { return applicationWrapper.Title; }
        }
        public IConfiguration ProductConfiguration
        {
            get { return applicationWrapper.ProductConfiguration; }
        }
        protected ApplicationWrapper applicationWrapper;
        protected void Application_Start(object sender, EventArgs e)
        {
            Run();
        }
        protected void Application_EndRequest(object sender, EventArgs e)
        {
            //ISessionStore sessionStore;
            //if (ComponentRegistry.TryResolve<ISessionStore>(out sessionStore))
            //{
            //    var sessions = ((ManagedWebSessionStore)sessionStore).FindAllCompatibleSession();
            //    foreach (var session in sessions)
            //    {
            //        session.Dispose();
            //    }
            //}
        }

        public IConfiguration Configuration
        {
            get { return applicationWrapper.Configuration; }
        }
        protected string path;
        public virtual string Path
        {
            get
            {
                if (string.IsNullOrEmpty(path))
                {
                    path = HttpRuntime.AppDomainAppPath;
                }
                return path;
            }
        }
        private Type webApplicationType;
        protected WebApplication()
            : this(null)
        {

        }
        protected WebApplication(string name)
        {
            webApplicationType = this.GetType();
            this.name = name;
            while (!webApplicationType.BaseType.Equals(typeof(WebApplication)))
            {
                if (string.IsNullOrEmpty(name))
                {
                    this.name = webApplicationType.BaseType.Assembly.GetName().Name;
                }
                webApplicationType = webApplicationType.BaseType;
            }
        }
        private string name;
        public string Name
        {
            get { return name; }
        }

        protected virtual void RegisterHttpConfig()
        {
            GlobalConfiguration.Configure(RegisterHttpRoute);

            GlobalConfiguration.Configure(RegisterFormatter);
            GlobalConfiguration.Configure(BindHttpActionSelector);
            GlobalConfiguration.Configure(RegisterMessageHandler);
            //RegisterFormatter(GlobalConfiguration.Configuration);
            //RegisterHttpRoute(GlobalConfiguration.Configuration);

        }
        protected virtual void RegisterMessageHandler(HttpConfiguration config)
        {
            config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.LocalOnly;
            //config.EnableQuerySupport();
        }
        protected virtual void BindHttpActionSelector(HttpConfiguration config)
        {
            config.Services.Replace(typeof(IHttpActionSelector), new HttpControllerActionSelector());

        }
        protected virtual void RegisterHttpRoute(HttpConfiguration config)
        {
            config.MapHttpAttributeRoutes();
            config.Routes.MapHttpRoute(
              name: "DefaultApi",
              routeTemplate: "api/{controller}/{id}",
              defaults: new { id = RouteParameter.Optional }
          );
        }
        protected virtual void RegisterFormatter(HttpConfiguration config)
        {
            for (int i = 0; i < config.Formatters.Count; i++)
            {
                if (GlobalConfiguration.Configuration.Formatters[i].GetType().Equals(typeof(JsonMediaTypeFormatter)))
                {
                    GlobalConfiguration.Configuration.Formatters[i] = new JsonGZipMediaTypeFormatter();
                }
            }
            //GlobalConfiguration.Configuration.Formatters.Add(new UnknownMediaTypeFormatter());

        }
        public virtual void Run()
        {
            foreach (var assemblyName in this.GetType().BaseType.Assembly.GetReferencedAssemblies())
            {
                Assembly.Load(assemblyName);
            }
            //HostingEnvironment.RegisterVirtualPathProvider(new ShareingVirtualPathProvider());
            applicationWrapper = new ApplicationWrapper(this);
            applicationWrapper.Initialize();

            RegisterHttpConfig();
            RegisterMvcConfig();
            RegisterAuthenticationClient();
        }
        protected virtual void RegisterAuthenticationClient()
        {
            OAuthWebSecurity.RegisterClient(new InovoutOpenIdClient(), InovoutOpenIdClient.OpenIdProviderName, new Dictionary<string, object>());
            OAuthWebSecurity.RegisterClient(new InovoutOAuthClient(), InovoutOAuthClient.OAuth2ClientProviderName, new Dictionary<string, object>());

        }
        protected virtual void RegisterMvcConfig()
        {
            RegisterGlobalFilters(GlobalFilters.Filters);

            RegisterRoutes(RouteTable.Routes);
            //BundleTable.Bundles.AddDefaultFileExtensionReplacements();
            //todo 验证min和debug是否可以找到对应的文件
            BundleTable.Bundles.Clear();
            BundleTable.Bundles.ResetAll();

            RegisterContents(BundleTable.Bundles);
            ControllerBuilder.Current.SetControllerFactory(ComponentRegistry.Resolve<IControllerFactory>());
            ModelMetadataProviders.Current = new ModelDescriptorModelMetadataProvider();
            HtmlHelper.ClientValidationEnabled = true;
            HtmlHelper.UnobtrusiveJavaScriptEnabled = true;
            DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(UniqueAttribute), typeof(UniqueAttributeAdapter));
            //ModelValidatorProviders.Providers.Clear();
            //ModelValidatorProviders.Providers.Add(new ModelDescriptorModelValidatorProvider());
            //ModelValidatorProviders.Providers.Add(new DataErrorInfoModelValidatorProvider());
            //ModelValidatorProviders.Providers.Add(new ClientDataTypeModelValidatorProvider());

            InitializeBinders();

        }
        protected virtual void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute());
        }
        protected virtual void RegisterContents(BundleCollection contents)
        {
            ContentResource.Load(this);
            foreach (ContentResource contentResource in ContentResource.StyleResources)
            {
                if (contentResource.HasCdnUri)
                {
                    contents.Add(new StyleBundle(contentResource.Uri, contentResource.CdnUri));
                }
                else if (contentResource.Exists)
                {
                    contents.Add(new StyleBundle(contentResource.Uri).Include(contentResource.IncludeContentResourceUris));
                }
            }
            foreach (ContentResource contentResource in ContentResource.ScriptResources)
            {
                if (contentResource.HasCdnUri)
                {
                    contents.Add(new ScriptBundle(contentResource.Uri, contentResource.CdnUri));
                }
                else if (contentResource.Exists)
                {
                    contents.Add(new ScriptBundle(contentResource.Uri).Include(contentResource.IncludeContentResourceUris));
                }
            }
        }
        private void InitializeBinders()
        {
            ModelBinders.Binders.DefaultBinder = ComponentRegistry.Resolve<IModelBinder>();
        }

        protected virtual void RegisterApplicationRoutes(RouteCollection routes)
        {
            routes.MapRoute(
              name: "WebApplicationDefault",
              url: "{controller}/{action}/{id}",
              defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
              namespaces: new string[] { webApplicationType.FullName.Substring(0, webApplicationType.FullName.LastIndexOf(".") + 1) + "Controllers" }
            );
            routes.MapRoute(
                  name: "GlobalDefault",
                  url: "{controller}/{action}/{id}",
                  defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
              );
        }
        private void RegisterRoutes(RouteCollection routes)
        {
            RouteTable.Routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            RouteTable.Routes.IgnoreRoute("{*favicon}", new { favicon = @"(.*/)?favicon.ico(/.*)?" });
            RegisterApplicationRoutes(routes);
        }
    }
}
